package com.lagou.sqlSession;

import com.lagou.config.BoundSql;
import com.lagou.pojo.Configuration;
import com.lagou.pojo.MappedStatement;
import com.lagou.utils.GenericTokenParser;
import com.lagou.utils.ParameterMapping;
import com.lagou.utils.ParameterMappingTokenHandler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class SimpleExecutor implements Executor{

    @Override
    public Object query(Configuration configuration, MappedStatement mappedStatement, Object... params) {
        //1.注册驱动，获取连接
        Connection connection = null;
        try {
            connection = configuration.getDataSource().getConnection();
        //2.获取sql语句:select * from user where id= #{id} and username=#{username}
        //转换成：select * from user where id=? and username=?
        String sql=mappedStatement.getSql();
        BoundSql boundSql=getBoundSql(sql);
        //3.获取预处理对象：preparedStatement
            PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
        //4.设置参数
            //获取到了参数的全路径
            String paramterType = mappedStatement.getParamterType();
            Class<?> paramtertypeClass=getClassType(paramterType);
            List<ParameterMapping> parameterMappingList=boundSql.getParameterMappingList();
            for (int i=0;i<parameterMappingList.size();i++) {
                ParameterMapping parameterMapping=parameterMappingList.get(i);
                String content=parameterMapping.getContent();
                //反射
                Field declaredField = paramtertypeClass.getDeclaredField(content);
                //私有属性暴力访问
                declaredField.setAccessible(true);
                Object o = declaredField.get(params[0]);
                preparedStatement.setObject(i+1,o);
            }
        //5.执行sql
            if(sql!=null&&!sql.contains("select")){
               Boolean b=preparedStatement.execute();
               return b;
            }else{
                ResultSet resultSet=preparedStatement.executeQuery();
                String resultType = mappedStatement.getResultType();
                Class<?> resultTypeClass=getClassType(resultType);
                ArrayList<Object> objects = new ArrayList<>();

                //6.封装返回结果集
                while (resultSet.next()){
                    Object o =resultTypeClass.newInstance();
                    //元数据
                    ResultSetMetaData metaData= resultSet.getMetaData();
                    for (int i=1;i<=metaData.getColumnCount();i++){
                        //字段名
                        String columnName = metaData.getColumnName(i);
                        //字段值
                        Object value=resultSet.getObject(columnName);
                        //会用反射或者内省，根据数据库表和实体的关系，完成封装
                        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                        Method writeMethod=propertyDescriptor.getWriteMethod();
                        writeMethod.invoke(o,value);
                    }
                    objects.add(o);
                }
                return (List) objects;
            }

        } catch (Exception throwables) {
            throwables.printStackTrace();
        }
       return null;
    }

    private Class<?> getClassType(String paramterType) throws ClassNotFoundException {
        if(paramterType!=null){
            Class<?> aClass=Class.forName(paramterType);
            return aClass;
        }
        return null;
    }

    /**
     * 将#{}的解析工作：1.将#{}使用？替换，
     * 解析出#{}里面的值进行储存
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        //标记处理类：配置标记解析器来完成对站位符的解析处理工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        //解析出来的sql
        String parseSql=genericTokenParser.parse(sql);
        //#{}里面解析出来的参数名称
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
        BoundSql boundSql=new BoundSql(parseSql,parameterMappings);
        return boundSql;

    }
}
